/*
 * HttpAuthentication.cpp
 */

#include "../../common/ServerConfig.h"
#include "HttpAuthentication.h"
#include "HttpHeader.h"
#include "HttpHost.h"
#include "HttpRequest.h"
#include "HttpResponse.h"
#include "HttpCodes.h"

#define BASE64_DECODED_BLOCKSIZE 3
#define BASE64_ENCODED_BLOCKSIZE (BASE64_DECODED_BLOCKSIZE * 8 / 6)
#define PADDING_CHR '='

const std::string HttpAuthentication::base64IndexTable =
	             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	             "abcdefghijklmnopqrstuvwxyz"
	             "0123456789+/";

HttpAuthentication::HttpAuthentication(const HttpRequest& msg,
		const ServerConfig& svCfg): request(msg), serverConfig(svCfg) {}

HttpAuthentication::~HttpAuthentication() {}

std::string HttpAuthentication::base64Decode(const std::string& cadena) {
	size_t longCad = cadena.size();
	std::string salida;
	if(longCad % BASE64_ENCODED_BLOCKSIZE == 0) {
		unsigned char indexes[BASE64_ENCODED_BLOCKSIZE];
		unsigned char decoded[BASE64_DECODED_BLOCKSIZE];
		salida.reserve(longCad);
		size_t i = 0;
		int j;
		while(i < longCad) {
			// Busco el index de los caracteres codificados en la tabla
			for (j = 0; j < BASE64_ENCODED_BLOCKSIZE; j++) {
				if(cadena[i + j] != PADDING_CHR)
					indexes[j] = this->base64IndexTable.find(cadena[i + j]);
				else
					indexes[j] = 0;
			}
			// Creo variables de 8 bits a partir de las de 6 bits según corresponda
			decoded[0] = (indexes[0] << 2) + ((indexes[1] & 0x30) >> 4);
			decoded[1] = ((indexes[1] & 0xf) << 4) + ((indexes[2] & 0x3c) >> 2);
			decoded[2] = ((indexes[2] & 0x3) << 6) + indexes[3];

			// Agrego la decodificacion a la salida si corresponde
			for (j = 0; j < BASE64_DECODED_BLOCKSIZE; j++) {
				if(cadena[i + j + 1] != PADDING_CHR)
					salida += decoded[j];
			}
			i += BASE64_ENCODED_BLOCKSIZE;
		}
	}
	return salida;
}

bool HttpAuthentication::isAuthenticated() {
	try {
		std::string authHeader = this->request.getHeader("Authorization");
		size_t sep = authHeader.find_first_of(' ');
		if (authHeader.substr(0, sep) == "Basic") {
			std::string userAndPass = this->base64Decode(authHeader.substr(sep + 1));
			size_t sep = userAndPass.find(':');
			std::string user(userAndPass.substr(0, sep));
			std::string pass(userAndPass.substr(sep + 1));
			if(serverConfig.isUserAuthorized(user, pass))
				return true;
		}
	} catch (const MessageException& e) {
		// No hay encabezado de autenticacion
	}
	return false;
}
